/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.uca.dss.curso1011.grupo3.gestionreservasextendido;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.uca.dss.curso1011.grupo3.estrategiaasientos.EstrategiaAsientos;
import org.uca.dss.curso1011.grupo3.estrategiaasientos.EstrategiaAsientosAleatoria;
import org.uca.dss.curso1011.grupo3.estrategiaasientos.EstrategiaAsientosIncremental;
import org.uca.dss.curso1011.grupo3.estrategiaasientos.EstrategiaAsientosNegativo;
import org.uca.dss.curso1011.grupo3.gestionreservas.GestionReservas;
import org.uca.dss.curso1011.grupo3.gestionreservas.Reserva;
import org.uca.dss.curso1011.grupo3.gestionreservas.Trayecto;
import org.uca.dss.curso1011.grupo3.interfazextendido.InterfazCompras;

/**
 * Esta clase representa la gestion de las reservas para los viajes donde exista la posibilidad de realizar
 * transbordo. Implementa a la interfaz InterfazCompras.
 * @author Adolfo Barroso, Levi Orta, Juan Manuel Chaves
 */
public final class GestionReservasExtendido extends GestionReservas implements InterfazCompras{

    //Atributo de la clase
    private EstrategiaAsientos estrategiaAsientos;

    /**
     *
     * Constructor de la clase GestionReservasExtendido. Esta clase hereda de GestionReservas
     * por lo que en su constructor sera necesario los string con el nombre de los ficheros donde
     * se encuentra los datos de los trenes y otro con los datos de los diferentes trayectos. Tambien es
     * necesario pasarle un string con el nombre de la estrategia que se va a seguir para llevar a cabo la
     * asignacion de asientos.
     *
     * @param ficheroTrenes: fichero donde se encuentra almacenado la informacion de los trenes
     * @param ficheroTrayecto: fichero donde se encuentra almacenado la informacion de los trayectos
     * @param estrategia: string con el nombre de la estrategia a seguir para la asignacion de asientos.
     */
    public GestionReservasExtendido(String ficheroTrenes, String ficheroTrayecto, String estrategia) throws FileNotFoundException, IOException
    {
        super(ficheroTrenes, ficheroTrayecto);

        if(estrategia.equals("aleatoria")){
            this.setEstrategiaAsientos(new EstrategiaAsientosAleatoria(this));
        }
        else if (estrategia.equals("incremental")){
            this.setEstrategiaAsientos(new EstrategiaAsientosIncremental(this));
        }
        else if(estrategia.equals("negativo")){
            this.setEstrategiaAsientos(new EstrategiaAsientosNegativo(this));
        }
        else{
            throw new IllegalArgumentException("Estrategia de asientos incorrecta");
        }
    }

    /**
     *
     * Constructor de la clase GestionReservasExtendido. Esta clase hereda de GestionReservas
     * por lo que en su constructor sera necesario los string con el nombre de los ficheros donde
     * se encuentra los datos de los trenes y otro con los datos de los diferentes trayectos. Si 
     * se usa dicho constructor la gestion de la asignacion de asientos que se va a llevar acabo es
     * la asignacion negativa (por defecto).
     *
     * @param ficheroTrenes: fichero donde se encuentra almacenado la informacion de los trenes
     * @param ficheroTrayecto: fichero donde se encuentra almacenado la informacion de los trayectos
     */
    public GestionReservasExtendido(String ficheroTrenes, String ficheroTrayecto) throws FileNotFoundException, IOException
    {
        super(ficheroTrenes, ficheroTrayecto);
        this.setEstrategiaAsientos(new EstrategiaAsientosNegativo(this));
    }

     /**
     * Permite realizar una reserva de un viaje, a partir de su itinerario. Se devuelve una lista con los códigos
     * de cada una de las reservas de los trayectos que son necesarios coger para realizar el viaje.
     *
     * En el caso de que el itinerario, debe de devolver
     * una excepción IllegalArgumentException
     *
     * @param fecha de la reserva
     * @param itinerario deseado
     * @return Lista de las reservas, permite cancelar parcialmente cada reserva.
     *
     */
    @Override
    public List<ReservaTrayecto> reservaAsiento(Itinerario itinerario, LocalDate fecha) {

        List<ReservaTrayecto> listaReservas = new ArrayList<ReservaTrayecto>();
        int numAsiento;
        String codigoReserva;
        ReservaTrayecto reservaTrayecto;

        //comprobamos que el itinerario es correcto
        if(itinerario.isEmpty() || itinerario.size() > 2){
            throw new IllegalArgumentException("Itinerario incorrecto");
        }
        //si el itinerario tiene tamano 1, no hay transbordo
        else if(itinerario.size() == 1){
                String origen = itinerario.get(0).getOrigen();
                String destino = itinerario.get(0).getDestino();
                LocalTime horaSalida = itinerario.get(0).getHoraSalida();

                numAsiento = this.estrategiaAsientos.getNumeroAsiento(this.trayectos.getTrayecto(origen, destino, horaSalida), fecha, horaSalida);

                codigoReserva = this.reservaAsiento(origen, destino, fecha, horaSalida, numAsiento);
                reservaTrayecto = new ReservaTrayecto(itinerario.get(0), fecha, numAsiento, codigoReserva);
                listaReservas.add(reservaTrayecto);
            }
            //si el itinerario tiene tamano 2, hay transbordo
            else{
                //reservamos el primer trayecto
                String origenPrimero = itinerario.get(0).getOrigen();
                String destinoPrimero = itinerario.get(0).getDestino();
                LocalTime horaSalidaPrimero = itinerario.get(0).getHoraSalida();

                numAsiento = this.estrategiaAsientos.getNumeroAsiento(this.trayectos.getTrayecto(origenPrimero, destinoPrimero, horaSalidaPrimero), fecha, horaSalidaPrimero);

                codigoReserva = this.reservaAsiento(origenPrimero, destinoPrimero, fecha, horaSalidaPrimero, numAsiento);
                reservaTrayecto = new ReservaTrayecto(itinerario.get(0), fecha, numAsiento, codigoReserva);
                listaReservas.add(reservaTrayecto);

                //reservamos el segundo trayecto
                String origenSegundo = itinerario.get(1).getOrigen();
                String destinoSegundo = itinerario.get(1).getDestino();
                LocalTime horaSalidaSegundo = itinerario.get(1).getHoraSalida();

                //si en el segundo trayecto esta libre el mismo asiento que en el primero
                if(asientoLibreTrayecto(numAsiento, this.trayectos.getTrayecto(origenSegundo, destinoSegundo, horaSalidaSegundo),fecha, horaSalidaSegundo)){
                    codigoReserva = this.reservaAsiento(origenSegundo, destinoSegundo, fecha, horaSalidaSegundo, numAsiento);
                    reservaTrayecto = new ReservaTrayecto(itinerario.get(1), fecha, numAsiento, codigoReserva);
                    listaReservas.add(reservaTrayecto);
                }
                else{
                    numAsiento = this.estrategiaAsientos.getNumeroAsiento(this.trayectos.getTrayecto(origenSegundo, destinoSegundo, horaSalidaSegundo), fecha, horaSalidaSegundo);
                    codigoReserva = this.reservaAsiento(origenSegundo, destinoSegundo, fecha, horaSalidaSegundo, numAsiento);
                    reservaTrayecto = new ReservaTrayecto(itinerario.get(1), fecha, numAsiento, codigoReserva);
                    listaReservas.add(reservaTrayecto);
                }
            }
        return listaReservas;
    }

     /**
     * Permite comprobar el número de asientos libres de un viaje,
     * considerando los distintos trayectos de un itinerario.
     *
     * En el caso de que alguno de los parámetros no sea correcto, debe de devolver
     * una excepción IllegalArgumentException
     *
     * @param fecha de la reserva
     * @param itinerario deseado
     * @return número de asientos libres
     *
     */
    @Override
    public int asientosLibres(LocalDate fecha, Itinerario itinerario) {
        int asientosLibres = 0;

        if(fecha.isBefore(new LocalDate()) || itinerario.isEmpty())
        {
            throw new IllegalArgumentException("Itinerario o fecha incorrectos");
        }
        else
        {
            asientosLibres = this.asientosLibres(itinerario.get(0).getOrigen(), itinerario.get(0).getDestino(), fecha, itinerario.get(0).getHoraSalida());
 
            int i = 1;
            while(i < itinerario.size())
            {
                int asientosLibresTrayecto = this.asientosLibres(itinerario.get(i).getOrigen(), itinerario.get(i).getDestino(), fecha, itinerario.get(i).getHoraSalida());
                
                if(asientosLibres > asientosLibresTrayecto)
                    asientosLibres = asientosLibresTrayecto;
                i++;
            }
        }
        return asientosLibres;
    }

    /**
     * Cancela una reserva para un trayecto independiente, dejando el asiento indicado libre
     *
     * @param reserva a cancelar
     */
    @Override
    public void cancelaReserva(ReservaTrayecto reserva) {
        this.cancelaReserva(reserva.getCodigoReserva());
    }

    /**
     * Cancela la reserva de un itinerario. Cancelando cada una de las reservas de los trayectos que lo componen.
     *
     * @param reservas lista de reservas a cancelar
     */
    @Override
    public void cancelaReserva(List<ReservaTrayecto> reservas) {
        for(int i = 0; i < reservas.size(); i++)
            this.cancelaReserva(reservas.get(i));
    }

    /**
     * Establece la estrategia que se va a seguir para la asignacion del numero
     * de asiento en la reserva
     *
     * @param estrategia, objeto del tipo EstrategiaAsientos que implementa la estrategia.
     */
    public void setEstrategiaAsientos(EstrategiaAsientos estrategia){
        this.estrategiaAsientos = estrategia;
    }

    /**
     * Determina si un numero de asiento dado esta libre en el trayecto o no.
     *
     * @paran numAsiento, numero de asiento que queremos conocer si esta libre o no.
     * @param trayecto, trayecto para el cual se quiere conocer si esta libre el asiento.
     * @param fecha, fecha del trayecto para el cual se quiere conocer si esta libre el asiento
     * @param horaSalida, hora del trayecto para el cual se quiere conocer si esta libre el asiento
     * @return Booleano que indicara si el asiento esta libre o no.
     */
    boolean asientoLibreTrayecto(int numAsiento, Trayecto trayecto, LocalDate fecha, LocalTime horaSalida)
    {
        List<Reserva> reservas = this.listaReservasTrayecto(trayecto, fecha, horaSalida);

        if(numAsiento <= 0 || numAsiento > trayecto.getTren().getNAsientos())
        {
            return false;
        }
        else
        {
            for(int i = 0; i < reservas.size(); ++i)
            {
                if(reservas.get(i).getnumAsiento() == numAsiento)
                    return false;
            }
            return true;
        }
    }
}
